package com.mimosa.util.extract;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mimosa.util.constant.StringConstUtil;

/**
 * 基于字典的规则抽取
 * 
 * @filename DictionaryExtractor.java
 * @datetime 2013年9月6日 下午5:23:41
 * @author ggeagle
 * 
 */
public class DictionaryExtractor extends Extractor {
	/**
	 * 日志记录器
	 */
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	/**
	 * 字典上下文环境
	 */
	private Map<Character, Object> rootContext = null;
	/**
	 * 叶子结点
	 */
	static final Character LEAF = '1';

	public DictionaryExtractor() {
		this.rootContext = new HashMap<Character, Object>();
	}

	/**
	 * 插入单个字典到字典树中
	 * 
	 * @param dicValue
	 *            字典值
	 */
	@SuppressWarnings("unchecked")
	public void insertDic(String dicValue) {
		dicValue = StringUtils.trimToEmpty(dicValue);
		if (StringUtils.isNotBlank(dicValue) && dicValue.length() > 1) {
			Map<Character, Object> parentContext = null;
			Map<Character, Object> currentContext = this.rootContext;
			dicValue = dicValue.toLowerCase();
			for (int i = 0; i < dicValue.length(); i++) {
				if (currentContext != null && currentContext.containsKey(dicValue.charAt(i))) {
					parentContext = currentContext;
					currentContext = (Map<Character, Object>) currentContext.get(dicValue.charAt(i));
				} else {
					currentContext = new HashMap<Character, Object>();
					parentContext.put(dicValue.charAt(i), currentContext);
				}
				if (i == dicValue.length() - 1) {
					parentContext.put(LEAF, Collections.EMPTY_MAP);
				}
			}
		}
	}

	/**
	 * 测试字典数据中是否包含指定的文本
	 * 
	 * @param text
	 *            要检测的文本
	 */
	@SuppressWarnings("unchecked")
	public boolean containDicValue(String text) {
		boolean result = false;
		if (StringUtils.isNotBlank(text)) {
			text = text.toLowerCase();
			Map<Character, Object> context = rootContext;
			int index = 0;
			for (; index < text.length(); index++) {
				if (context != null && context.containsKey(text.charAt(index))) {
					context = (Map<Character, Object>) context.get(text.charAt(index));
				}
			}
			if (index == text.length()) {
				result = true;
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<ExtractHit> extract(String inputText) {
		Map<String, ExtractHit> result = new HashMap<String, ExtractHit>();
		if (StringUtils.isNotBlank(inputText)) {
			String originalText = inputText;
			inputText = inputText.toLowerCase();
			inputText = inputText + StringConstUtil.SPACE;
			int startIndex = 0;
			int currentIndex = 0;
			int wordLength = 0;
			Map<Character, Object> context = this.rootContext;
			Map<Character, Object> prevContext = context;
			Character queryChar = null;
			while (true) {
				queryChar = inputText.charAt(currentIndex);
				if (context.containsKey(queryChar)) {
					wordLength++;
					prevContext = context;
					try {
						context = (Map<Character, Object>) context.get(queryChar);
					} catch (java.lang.ClassCastException e) {
						logger.error("class cast error occured!", e);
						System.exit(-1);
					}
					currentIndex++;
				} else {
					if (wordLength > 1 && prevContext.containsKey(LEAF) && isBoundary(queryChar) && (startIndex == 0 || isBoundary(inputText.charAt(startIndex - 1)))) {
						String dicValue = originalText.substring(startIndex, currentIndex);
						ExtractHit extractHit = result.get(dicValue);
						if (extractHit == null) {
							extractHit = new ExtractHit();
							extractHit.setValue(dicValue);
							result.put(dicValue, extractHit);
						}
						extractHit.setCount(extractHit.getCount() + 1);
						extractHit.addIndexPoint(startIndex);
						result.put(dicValue, extractHit);
					} else {
						currentIndex++;
					}
					wordLength = 0;
					startIndex = currentIndex;
					context = this.rootContext;
					prevContext = context;
				}
				if (currentIndex == inputText.length()) {
					break;
				}
			}
		}
		return result.values();
	}

	boolean isBoundary(char ch) {
		boolean result = true;
		String chStr = String.valueOf(ch);
		if (chStr.matches("[0-9A-Za-z-]")) {
			result = false;
		}
		return result;
	}

	@Override
	public void clear() {
		this.rootContext.clear();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void createByDicSettingEntity(Collection<String> entityList) {
		if (entityList != null && entityList.size() > 0) {
			Map<Character, Object> parentContext = null;
			Map<Character, Object> currentContext = null;
			Character queryChar = null;
			for (String dic : entityList) {
				dic = StringUtils.trimToEmpty(dic).toLowerCase();
				if (dic.length() < 2) {
					continue;
				}
				currentContext = rootContext;
				for (int i = 0; i < dic.length(); i++) {
					parentContext = currentContext;
					queryChar = dic.charAt(i);
					if (currentContext.containsKey(dic.charAt(i))) {
						currentContext = (Map<Character, Object>) currentContext.get(queryChar);
					} else {
						currentContext = new HashMap<Character, Object>();
						parentContext.put(queryChar, currentContext);
					}
					if (i == dic.length() - 1) {
						parentContext.put(LEAF, Collections.EMPTY_MAP);
					}
				}
			}
		}
	}
}